/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "GravityEvent.h"
#include <math/utl.h>
#include <lib3d/Math_lib3d.h>
#include <xml/Saver.h>

namespace liba
{
	namespace lib3d
	{
		namespace particles
		{
			GravityEvent::GravityEvent() : _time(0), _gravity(0.0f, -9.8f, 0.0f), _relative(false)
			{
			}
			
			GravityEvent::~GravityEvent()
			{
			}
			
			void GravityEvent::set_gravity(const math::Vector3<Float> & value)
			{
				_gravity = value;
			}

			math::Vector3<Float> GravityEvent::get_gravity()
			{
				return _gravity;
			}

			void GravityEvent::set_time(float value)
			{
				_time = value;
			}

			void GravityEvent::update_particle(Particle * particle, Float delta_time, KeyFrameIterator base_frame, KeyFrameIterator next_frame, KeyFrameArray * frames)
			{
				// current gravity vector
				math::Vector3<Float> gravity = _gravity;
				
				// if this frame interpolates with other
				if(next_frame != frames->end() && next_frame->get_event<GravityEvent>()->_relative)
				{
					// compute interpolation factor
					Float factor = (particle->lifeTime - base_frame->time) / (next_frame->time - base_frame->time);
					
					// iterpolate
					gravity = math::lerp(gravity, next_frame->get_event<GravityEvent>()->_gravity, factor);
				}
				
				particle->currentState.position += gravity * delta_time;
			}
			
			Atom GravityEvent::get_type()
			{
				static Atom type("GRAVITY");
				return type;
			}
			
			Float GravityEvent::get_time()
			{
				return _time;
			}
			
			void GravityEvent::on_save_attributes(xml::SaverBase * saver) const
			{
				if(_time > 0.0f)
					saver->save_attribute("Time", _time);

				if(_gravity != lib3d::Vector(0.0f, -9.8f, 0.0f))
					saver->save_attribute("Value", _gravity);

				if(!_relative)
					saver->save_attribute("Relative", _relative);
			}

			bool GravityEvent::on_attribute(xml::Provider * provider, const std::string & name, const std::string & value)
			{
				// gravity vector
				if(name == "Value")
					return converter::convert(value, &_gravity);
				
				// frame time
				if(name == "Time")
					return converter::convert(value, &_time);
				
				// if gravity relative to previous frame
				if(name == "Relative")
					return converter::convert(value, &_relative);
				
				// unknown attribute
				return false;
			}
		}
	}
}